package com.fary.beans.factory.support;

import com.fary.core.SpringException;
import com.fary.beans.factory.FactoryBean;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    /**
     * Cache of singleton objects created by FactoryBeans: FactoryBean name to object.
     */
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);

    /**
     * 从工厂缓存中获取实例对象
     */
    protected Object getCachedObjectForFactoryBean(String beanName) {
        return this.factoryBeanObjectCache.get(beanName);
    }

    /**
     * 从工厂实例对象创建实例对象
     */
    protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
        // 1.如果是单例，并且已经存在于单例对象缓存中
        if (factory.isSingleton() && containsSingleton(beanName)) {
            // singletonObjects作为锁对象
            synchronized (getSingletonMutex()) {
                // 2.从FactoryBean创建的单例对象的缓存中获取该bean实例
                Object object = this.factoryBeanObjectCache.get(beanName);
                if (object == null) {
                    // 3.调用FactoryBean的getObject方法获取对象实例
                    object = doGetObjectFromFactoryBean(factory, beanName);
                    // 再次从缓存中获取，看有没有其他线程创建好的
                    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                    // 如果有值，代表已经经过后处理了，可以直接返回
                    // 4.如果该beanName已经在缓存中存在，则将object替换成缓存中的
                    if (alreadyThere != null) {
                        object = alreadyThere;
                    } else {
                        // 没有经过后处理，首先看是不是用户自己创建的
                        if (shouldPostProcess) {
                            if (isSingletonCurrentlyInCreation(beanName)) {
                                // Temporarily return non-post-processed object, not storing it yet..
                                return object;
                            }
                            // 向正在创建单例集合中添加正在创建实例对象的beanName
                            beforeSingletonCreation(beanName);
                            try {
                                // 5.对bean实例进行后置处理，执行所有已注册的BeanPostProcessor的postProcessAfterInitialization方法
                                object = postProcessObjectFromFactoryBean(object, beanName);
                            } catch (Throwable ex) {
                                throw new SpringException(beanName + " Post-processing of FactoryBean's singleton object failed", ex);
                            } finally {
                                // 移除正在创建实例对象集合中的beanName
                                afterSingletonCreation(beanName);
                            }
                        }
                        // 6.将beanName和object放到factoryBeanObjectCache缓存中
                        if (containsSingleton(beanName)) {
                            this.factoryBeanObjectCache.put(beanName, object);
                        }
                    }
                }
                // 7.返回object对象实例
                return object;
            }
        } else {
            // 8.调用FactoryBean的getObject方法获取对象实例
            Object object = doGetObjectFromFactoryBean(factory, beanName);
            if (shouldPostProcess) {
                try {
                    // 9.对bean实例进行后置处理，执行所有已注册的BeanPostProcessor的postProcessAfterInitialization方法
                    object = postProcessObjectFromFactoryBean(object, beanName);
                } catch (Throwable ex) {
                    throw new SpringException(beanName + " Post-processing of FactoryBean's object failed", ex);
                }
            }
            // 10.返回object对象实例
            return object;
        }
    }

    /**
     * 从工厂实例对象创建实例对象
     */
    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws SpringException {
        Object object;
        try {
            object = factory.getObject();
        } catch (SpringException ex) {
            throw new SpringException("BeanCurrentlyInCreationException" + beanName, ex);
        } catch (Throwable ex) {
            throw new SpringException(beanName + " FactoryBean threw exception on object creation", ex);
        }
        // 如果从工厂对象中获取的实例对象为null，则返回NullBean
        if (object == null) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                throw new SpringException(beanName + " FactoryBean which is currently in creation returned null from getObject");
            }
            object = new NullBean();
        }
        return object;
    }

    protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
        try {
            return factoryBean.getObjectType();
        } catch (Throwable ex) {
            return null;
        }
    }

    @Override
    protected void clearSingletonCache() {
        synchronized (getSingletonMutex()) {
            super.clearSingletonCache();
            this.factoryBeanObjectCache.clear();
        }
    }

    protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws SpringException {
        return object;
    }
}